<template>
  <div class="audio-player fill">
    <audio
        @canplay="handleCanplay"
        @timeupdate="handleTimeUpdate"
        @pause="playing=false"
        @play="playing=true"
        @error="handleError"
        @ended="handleNext(true)"
        ref="player"
        :src="src"
    />
    <div class="controls flex-col">
      <div class="play-bar" ref="playBar" @click="handleChangeCurrent">
        <div class="total-width fill"/>
        <div class="current-width h-fill" :style="{width: currentWidth}"/>
        <div
            @mousedown="handleMoveStart"
            ref="playPoint"
            class="current-point"
            :style="{left: currentWidth}"/>
      </div>
      <div class="info-bar pn flex-row flex-1">
        <div class="left h-fill flex-row items-center">
          <template v-if="currentSong && currentSong.singer">
            <img style="height: 100%; width: auto" class="mnr" :src="cover" alt="">
            <div class="songInfo h-fill flex-1 flex-col justify-between">
              <div class="name flex-row items-center">
                <div class="songname flex-shrink-0">{{ currentSong.name }}</div>
                <div class="text-info mnx"> -</div>
                <SingerDisplay class="text-info" :singer="currentSong.singer"/>
              </div>
              <div>
                <ILike class="cursor-pointer" :size="20" :like="isLike" @click.native="handleToggleLike"/>
                <WIcon @click.native="handleDownload" class="mnl cursor-pointer" :size="20" icon="icon-xiazai"/>
              </div>
            </div>
          </template>

        </div>
        <div class="center flex-row justify-center items-center">
          <div>
            <WIcon @click.native="mode=2" v-show="mode===1" class="cursor-pointer" size="30" icon="icon-loop"/>
            <WIcon @click.native="mode=3" v-show="mode===2" class="cursor-pointer" size="30" icon="icon-singlecycle"/>
            <WIcon @click.native="mode=1" v-show="mode===3" class="cursor-pointer" size="30" icon="icon-random"/>

          </div>
          <WIcon @click.native="handlePrev(false)" class="mnx cursor-pointer" size="40" icon="icon-previous"/>
          <div class="play mnx cursor-pointer" @click="handleTogglePlay">
            <WIcon key="1" size="30" icon="icon-xiamimusic" v-if="!playing"/>
            <WIcon key="2" size="30" icon="icon-play" v-else/>
          </div>
          <WIcon @click.native="handleNext(false)" class="mnx cursor-pointer" size="40" icon="icon-nextsong"/>
          <div class="vol" >
            <WIcon @click.native="showVol=!showVol" class="cursor-pointer" size="30" icon="icon-volume"/>
            <div v-show="showVol" class="volBar flex-center flex-col">
              <div class="line" ref="volLine">
                <div class="point" ref="volPoint"></div>
              </div>
              <span class="text-success pst">{{vol}}</span>
            </div>
          </div>
        </div>
        <div class="right flex-row items-center justify-end pnr text-info">
          <div class="duration mlr">{{ currentTime|timeFilter }}/{{ duration|timeFilter }}</div>
          <div @click="toggleShowPlayList" class="btn flex-row items-center cursor-pointer">
            <WIcon class="" :size="25" icon="icon-musiclist"/>
            {{ list.length }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import {ipcRenderer} from "electron"
import {Component, Inject, Vue, Watch,} from "vue-property-decorator";
import {Song} from "@/ts.d/classes/Song";
import {qqMusicApi} from "@/network/qqmusic";
import WIcon from "@/components/icon/WIcon.vue";
import SingerDisplay from "@/components/listDisplay/SingerDisplay.vue";
import ILike from "@/components/icon/ILike.vue";
import {Action, Mutation, State} from "vuex-class";
import {events} from "@/ts.d/native/events";
import * as fs from "fs"
import * as Buffer from "buffer";

function createMovableBox(moveArea: HTMLElement, hotArea: HTMLElement, limit: {
  xMove?: boolean;
  yMove?: boolean;
  left?: number;
  right?: number;
  top?: number;
  bottom?: number }, callback?: Function) {
  let mouseX: number, mouseY: number, clientX: number, clientY: number
  hotArea.addEventListener("mousedown", (e: MouseEvent) => {
    mouseX = e.clientX
    mouseY = e.clientY
    clientX = moveArea.offsetLeft
    clientY = moveArea.offsetTop
    window.addEventListener("mousemove", mouseMove)
    window.addEventListener("mouseup", e => {
      window.removeEventListener("mousemove", mouseMove)
    })
  })
  let mouseMove = (e: MouseEvent) => {
    let deltaX = e.clientX - mouseX
    let deltaY = e.clientY - mouseY
    let oriLeft = deltaX + clientX
    let oriTop = deltaY + clientY
    if (limit.left !== undefined) {
      oriLeft = Math.max(limit.left, oriLeft)
    }
    if (limit.right !== undefined) {
      oriLeft = Math.min(limit.right, oriLeft)
    }
    if (limit.top !== undefined) {
      oriTop = Math.max(limit.top, oriTop)
    }
    if (limit.bottom !== undefined) {
      oriTop = Math.min(limit.bottom, oriTop)
    }
    if(limit.xMove){
      moveArea.style.left = oriLeft + "px"
    }
    if(limit.yMove){
      moveArea.style.top = oriTop + "px"
    }
    if(callback){
      callback(oriLeft, oriTop)
    }
  }
}


@Component({
  components: {ILike, SingerDisplay, WIcon},
  filters: {
    timeFilter(val: number) {
      let min = parseInt((val / 60).toString())
      let sec = parseInt((val - min * 60).toString())
      return `${(min.toString()).padStart(2, "0")}:${(sec.toString().padStart(2, "0"))}`
    }
  }
})
export default class Player extends Vue {
  @Action("nextSong") nextSong!: (song: Song) => void

  @Action("toggleLike") toggleLike!: (payload: { type: string, item: Song }) => void

  @Mutation("setCurrentPlayList") setCurrentPlayList !: (list: Song[]) => void

  @Inject() toggleShowPlayList!: () => void

  @State("like") like !: { song: Song[] }

  @Watch("vol") onVolChange(vol: number) {
    this.player.volume = vol / 100
  }

  @Action("download") download!: (meta: {url: string; fileName: string; fileSign: any; info: Song }) => void

  @Mutation("delLocalSong") delLocalSong !: (index: number) => void

  vol = 100
  index = 0
  list: Song[] = []
  onMove = false
  currentTime = 0
  duration = 0
  showVol = true
  playing = false
  mode: number = 1  // 1列表循环   2单曲循环  3随机播放

  get isLike() {
    const i = this.like.song.findIndex(item => item.mid === this.currentSong.mid)
    return i !== -1
  }

  get player(): HTMLAudioElement {
    return this.$refs.player as HTMLAudioElement
  }

  get currentSong(): Song {
    return this.list[this.index] ? this.list[this.index] : {} as Song
  }

  get src(): string{
    if(!this.currentSong.src){
      return ""
    }else{
      if(!this.currentSong.src.startsWith("http")){
        try {
          const buffer = fs.readFileSync(this.currentSong.src)
          const blob = new Blob([buffer])
          console.log(`---加载本地文件---${this.currentSong.src}`)
          return URL.createObjectURL(blob)
        } catch (e) {
          console.log(`---失败---${this.currentSong.src}---没有源文件`)
          this.delLocalSong(this.index)
          return  ""
        }
      }else{
        return  this.currentSong.src
      }
    }
  }

  get currentWidth(): string {
    if (this.duration === 0) return "0"
    return (this.currentTime / this.duration * 100).toFixed(2) + "%"
  }

  get cover() {
    return `https://y.gtimg.cn/music/photo_new/T002R300x300M000${this.currentSong.albumMid}.jpg`
  }

  start(list: Song[], index: number) {
    const same = list.length === this.list.length && this.list[index].mid === list[index].mid
    if (!same) {
      if(list.every(song=> song.src && !song.src.startsWith("http"))){
        this.setCurrentPlayList(list)
        this.list = list
        this.index = index
      }else{
        const perPieceCount = 70
        const pieceCount = parseInt(list.length / perPieceCount + "")
        let requestList = []
        for (let i = 0; i <= pieceCount; i++) {
          requestList.push(qqMusicApi.getSongPlaySrc.bind(this, list.slice(i * perPieceCount, (i + 1) * perPieceCount).map(item => item.mid)))
        }
        Promise.all(requestList.map(item => item.call(this))).then(value => {
          let ret: { [propsKey: number]: string } = value.reduce((p, c) => {
            p = Object.assign(p, c.data)
            return p
          }, {})
          list.forEach(song => song.src = ret[song.mid])
          console.log(`请求长度[${list.length}],返回长度[${Object.keys(ret).length}]`)
          if (Object.keys(ret).length === 0) {
            console.log(`返回长度为0,取消播放`)
            return
          }
          this.setCurrentPlayList(list)
          this.list = list
          this.index = index
        }).catch(() => {
          console.log("获取链接失败")
        })
      }
    } else {
      if (index === this.index) {
        this.handleTogglePlay()
      } else {
        if (this.list[index].src) {
          this.index = index
        } else {
          console.log(`歌曲[${this.list[index].name}],无播放源,切换失败`)
        }
      }
    }

  }

  handleCanplay(): void {
    this.duration = this.player.duration as number
    (this.$refs.player as any).play()
    this.nextSong(this.currentSong)
  }

  handleTimeUpdate(e: { target: HTMLAudioElement }) {
    if (!this.onMove) {
      this.currentTime = e.target.currentTime
    }
  }

  // 直接点选进度条
  handleChangeCurrent(e: MouseEvent) {
    if (e.target === this.$refs.playPoint || this.onMove) {
      return
    }
    const barWidth = (this.$refs as any).playBar.clientWidth
    const offsetX = e.offsetX
    const percent = offsetX / barWidth
    this.player.currentTime = this.duration * percent
  }


  // 移动进度条
  handleMoveStart(e: MouseEvent) {

    const barWidth = (this.$refs as any).playBar.clientWidth
    const target = <HTMLElement>this.$refs.playPoint
    this.onMove = true
    const startX = e.x
    const offsetX = target.offsetLeft


    const mouseUpHandler = () => {
      const percent = target.offsetLeft / barWidth
      this.player.currentTime = this.duration * percent
      this.onMove = false
      window.removeEventListener("mousemove", moveHandler)
      window.removeEventListener("mouseup", mouseUpHandler)
    }

    const moveHandler = (e: MouseEvent) => {
      const delta = e.x - startX
      let endX = offsetX + delta
      endX = Math.max(endX, 0)
      endX = Math.min(endX, barWidth)
      const percent = endX / barWidth
      this.currentTime = this.duration * percent
      target.style.left = percent * 100 + "%"
    }

    window.addEventListener("mousemove", moveHandler)
    window.addEventListener("mouseup", mouseUpHandler)
  }

  // 错误处理
  handleError() {
    console.log(this.currentSong.name, "-error")
  }

  // 播放状态切换
  handleTogglePlay() {
    if (!this.currentSong.src) {
      console.log(`歌曲[${this.currentSong.name}],无播放源`)
      return
    }
    if (this.playing) {
      this.player.pause()
    } else {
      this.player.play()
    }
  }

  // 通过歌曲无效
  osNotification(title: string, body: string) {
    const notify = new Notification(title, {body})
  }

  // 下一首
  handleNext(auto: undefined | boolean) {
    // 1列表循环   2单曲循环  3随机播放
    switch (this.mode) {
      case 1:
        this.index = this.index === this.list.length - 1 ? 0 : this.index + 1;
        break;
      case 2:
        if (auto) {
          this.player.play()
        } else {
          this.index = this.index === this.list.length - 1 ? 0 : this.index + 1;
          break;
        }
        break;
      case 3:
        this.index = Math.floor(Math.random() * this.list.length) + 1
    }
    if (!this.list[this.index].src) {
      console.log(`歌曲[${this.currentSong.name}],无播放源,切换下一首`)
      this.handleNext(false)
    }
  }

  // 上一首
  handlePrev(auto: undefined | boolean) {
    // 1列表循环   2单曲循环  3随机播放
    switch (this.mode) {
      case 1:
        this.index = this.index === 0 ? this.list.length - 1 : this.index - 1;
        break;
      case 2:
        if (auto) {
          this.player.play()
        } else {
          this.index = this.index === 0 ? this.list.length - 1 : this.index - 1;
          break;
        }
        break;
      case 3:
        this.index = Math.floor(Math.random() * this.list.length) + 1
    }
    if (!this.list[this.index].src) {
      this.handlePrev(auto)
    }
  }

  // 切换是否喜欢
  handleToggleLike() {
    this.toggleLike({type: "song", item: this.currentSong})
  }

  // 注册系统事件
  registerEvent() {
    ipcRenderer.on(events.TOGGLE_PLAY, this.handleTogglePlay)
  }

  // 下载歌曲
  handleDownload() {
    this.download({fileName: this.currentSong.name, url: this.currentSong.src as string, fileSign: this.currentSong.mid, info: this.currentSong})
  }

  mounted() {
    this.registerEvent()
    createMovableBox(this.$refs.volPoint as HTMLElement, this.$refs.volPoint as HTMLElement, {
      yMove: true,
      top: 0,
      bottom: (this.$refs.volLine as HTMLElement).offsetHeight - 8
    }, (x: number ,y: number) => {
      this.vol = parseInt((101 - y) / 1.01 + "")
    })
    this.showVol =false
  }
}
</script>

<style scoped lang="scss">
@import "~@/assets/css/var";

.audio-player {
  height: 100px;
  position: relative;
  user-select: none;
  color: white;
}

.controls {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 100%;

  .play-bar {
    margin: 10px auto;
    width: 90%;
    height: 5px;
    position: relative;
    cursor: pointer;
    //border: 1px solid red;
    .total-width {
      position: absolute;
      background-color: #29e7fe;
      cursor: pointer;
    }

    .current-width {
      position: absolute;
      background-color: #909399;
      width: 0;
    }

    .current-point {
      position: absolute;
      width: 15px;
      height: 15px;
      top: 50%;
      border-radius: 50%;
      background-color: blueviolet;
      border: 1px solid white;
      transform: translate(-50%, -50%);
      cursor: pointer;
    }
  }

  .info-bar {
    height: 55px;
    position: relative;

    .left {
      width: 40%;
      overflow: hidden;

      .songInfo {

      }
    }

    .center {
      width: 30%;

      .play {
        padding: $pn;
        border-radius: 50%;
        background-color: $text-primary;
      }

      .vol {
        position: relative;
        .volBar {
          position: absolute;
          width: 40px;
          height: 150px;
          background-color: #804d13;
          transform: translate(-50%, -100%);
          left: 50%;
          top: 0;
          padding: $pn;

          .line {
            position: relative;
            width: 3px;
            height: 100%;
            background-color: #29e7fe;

            .point {
              position: absolute;
              width: 10px;
              height: 10px;
              border: 1px solid white;
              cursor: pointer;
              border-radius: 50%;
              background-color: #5b3cc4;
              left: 50%;
              transform: translateX(-50%);
            }
          }
        }
      }
    }

    .right {
      width: 40%;

      .btn:hover {
        color: white;
      }
    }
  }

  .playList {
    color: $text-info;
    overflow: hidden;
  }
}
</style>
